/* 
Decorator 装饰器是一项实验性特性，在未来的版本中可能会发生改变
它们不仅增加了代码的可读性，清晰地表达了意图，而且提供一种方便的手段，增加或修改类的功能

若要启用实验性的装饰器特性，你必须在命令行或tsconfig.json里启用编译器选项

"experimentalDecorators": true,                 // Enable experimental support for legacy experimental decorators.        装饰器语法限制
"emitDecoratorMetadata": true,                  // Emit design-type metadata for decorated declarations in source files.  装饰器语法限制

装饰器
装饰器是一种特殊类型的声明，它能够被附加到类声明，方法， 访问符，属性或参数上。

https://xiaoman.blog.csdn.net/article/details/122630296
https://www.bookstack.cn/read/es6-3rd/spilt.1.docs-decorator.md
*/

// 1.类装饰器  ClassDecorator target 构造函数也就是会被装饰的类
// 2.属性装饰器  PropertyDecorator target 原型对象，不是构造函数
// 3.参数装饰器  ParameterDecorator target 原型对象，不是构造函数
// 4.方法装饰器  MethodDecorator target 原型对象，不是构造函数
// 5.装饰器工厂 又名函数科里化
// 6.import 'reflect-metadata'  元数据库
// 7.axios  封装了node环境和浏览器环境的请求方法



// 1.类装饰器  ClassDecorator target 构造函数

/* 
  @decorator

  class A {}

  // 等同于
  class A {}

  A = decorator(A) || A;
  也就是说，装饰器是一个对类进行处理的函数。
  装饰器函数的第一个参数，就是所要装饰的目标类。

  为类添加一个静态属性，如果想添加实例属性，可以通过目标类的prototype对象操作。
  target.prototype.xxx = "xxx"
*/

/* 
const Base: ClassDecorator = (target) => {
  target.prototype.xiaoli = "xiaoli"
  target.prototype.fn = () => {
    console.log("我是哈哈哈")
  }
}

@Base // 也可以写成Base(Http)
class Http {
  // 原来类的属性和方法
}

const http = new Http() as any
http.fn() 
*/

// 5.装饰器工厂 又名函数科里化
const Base = (name: string) => {
  const fn: ClassDecorator = (target) => {
    // console.log(target)
    target.prototype.name = name
    target.prototype.fn = () => {
      // console.log("我是哈哈哈")
    }
  }

  return fn
}

import axios from "axios"
import "reflect-metadata"

const Get = (url: string) => {
  const fn: MethodDecorator = (target, key: any, descriptor: PropertyDescriptor) => {
    // console.log(target, key, descriptor)  // {} getList { value: [Function: getList], writable: true, enumerable: false, configurable: true }
    // target.prototype.url = url // 不能这样写，因为target是原型对象，不是构造函数
    // target.__proto__.url = url // 不能这样写，因为target是原型对象，不是构造函数
    const k = Reflect.getMetadata('key', target)  // result
    axios.get(url).then(res => {
      // descriptor.value(res.data)
      descriptor.value(k ? res.data[k] : res.data)
    })
  }

  return fn
}

// 参数的装饰器先执行，再执行方法的装饰器
const Result = () => {
  const fn:ParameterDecorator = (target, key, index) => {
    Reflect.defineMetadata('key', 'result', target)
    // console.log(target, key, index)   // {} getList 0
  }

  return fn
}

const Name:PropertyDecorator = (target: any, key) => {
  // console.log(target, key)    // {} xiaoman
}

@Base("xiaoli")
class Http {
  @Name
  xiaoman:string
  constructor () {
    this.xiaoman = '小满'
  }
  @Get('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
  getList(@Result() data:any) {
    // console.log(data.result.list)
    console.log(data.list, 5555)
  }

  create() {}
}

const http = new Http() as any
// http.fn()
// console.log(http.name)
console.log(http.getList("111111"), 3333)








